/*
字符串

Rust 中有两种字符串类型：String 和 &str。
String 被存储为由字节组成的 vector（Vec<u8>），但保证了它一定是一个有效的 UTF-8 序列。
String 是堆分配的，可增长的，且不是零结尾的（null terminated）。

&str 是一个总是指向有效 UTF-8 序列的切片（&[u8]），并可用来查看 String 的内容，
就如同 &[T] 是 Vec<T> 的全部或部分引用。
*/

use std::str;

fn main() {
    // （所有的类型标注都不是必需的）
    // 一个对只读内存中分配的字符串的引用
    let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
    println!("pangram: {}", pangram);

    // 逆序迭代单词，这里并没有分配新的字符串
    println!("Words in reverse");
    for word in pangram.split_whitespace().rev() {
        println!("> {}", word);
    }

    // 复制字符到一个 vector，排序并移除重复值
    let mut chars: Vec<char> = pangram.chars().collect();
    chars.sort();
    chars.dedup();
    //println!("chars: {:?}", chars);

    // 创建一个空的且可增长的 `String`
    let mut string = String::new();
    for c in chars {
        // 在字符串的尾部插入一个字符
        string.push(c);
        // 在字符串尾部插入一个字符串
        string.push_str(", ");
    }

    //println!("string: {}", string);

    // 这个缩短的字符串是原字符串的一个切片，所以没有执行新的分配操作
    let chars_to_trim: &[char] = &[' ', ','];
    let trimmed_str: &str = string.trim_matches(chars_to_trim);
    println!("Used characters: {}", trimmed_str);

    // 堆分配一个字符串
    let alice = String::from("I like dogs");
    // 分配新内存并存储修改过的字符串
    let bob: String = alice.replace("dog", "cat");

    println!("Alice says: {}", alice);
    println!("Bob says: {}", bob);

    /*
    字面量与转义字符
    书写含有特殊字符的字符串字面量有很多种方法。它们都会产生类似的 &str，所以最好选择最方便的写法。
    类似地，字节串（byte string）字面量也有多种写法，它们都会产生 &[u8; N] 类型。

    通常特殊字符是使用反斜杠字符 \ 来转义的，这样你就可以在字符串中写入各种各样的字符，甚至是不可打印的
    字符以及你不知道如何输入的字符。如果你需要反斜杠字符，再用另一个反斜杠来转义它就可以，像这样：\\。

    字面量中出现的字符串或字符定界符必须转义："\""、'\''。
    */

    // 通过转义，可以用十六进制值来表示字节
    let byte_escape = "I'm writing \x52\x75\x73\x74";
    println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);

    // 也可以使用 Unicode 码位表示
    let unicode_codepoint = "\u{211D}";
    let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";

    println!("Unicode character {} (U+211D is called {}", unicode_codepoint, character_name);

    let long_string = "String literals
                        can span multiple lines.
                        The linebreak andd indentation here ->\
                        <- can be escaped too!";
    println!("{}", long_string);

    // 有时会有太多需要转义的字符，或者是直接原样写出会更便利。这时可以使用原始字符串（raw string）
    let raw_str = r"Escapes don't work here: \x3F \u{211D}";
    println!("{}", raw_str);

    // 如果你要在原始字符串中写引号，请在两边加一对 #
    let quotes = r#"And then I said: "There is no escape!""#;
    println!("{}", quotes);

    // 如果字符串中需要写 "#，那就在定界符中使用更多的 #
    // 可使用的 # 的数目没有限制。
    let long_delimiter = r###"A string with "# in it. And even "##!"###;
    println!("{}", long_delimiter);

    /*
    想要非 UTF-8 字符串（记住，&str 和 String 都必须是合法的 UTF-8 序列），
    或者需要一个字节数组，其中大部分是文本？请使用字节串（byte string）！
    */
    // 注意这并不是一个 &str
    let bytestring: &[u8; 20] = b"this is a bytestring";

    // 字节串没有实现 Display，所以它们的打印功能有些受限
    println!("A bytestring: {:?}", bytestring);

    // 字节串可以使用单字节的转义字符...
    let escaped = b"\x52\x75\x73\x74 as bytes";
    // ...但不能使用 Unicode 转义字符
    // let escaped = b"\u{211D} is not allowed";
    println!("Some escaped bytes: {:?}", escaped);

    // 原始字节串和原始字符串的写法一样
    let raw_bytestring = br"\u{211D} is not escaped here";
    println!("{:?}", raw_bytestring);

    // 把字节串转换为 &str 可能失败
    if let Ok(my_str) = str::from_utf8(raw_bytestring) {
        println!("And the same as text: '{}'", my_str);
    }

    // 字节串可以不使用 UTF-8 编码
    let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82";
    // 但这样的话它们就无法转换成 &str 了
    match str::from_utf8(shift_jis) {
        Ok(my_str) => println!("ok: '{}'", my_str),
        Err(e) => println!("err: {:?}", e),
    };
}